#pragma once
#include <string>
#include <vector>
#include <fstream>
#include <unordered_set>
#include <boost/algorithm/string.hpp>
#include <sys/time.h>

namespace common {

// 这个类包含了所有和字符串相关的操作
class StringUtil {//放置和字符串相关的类
public:
  // 例如, 如果待切分字符串:
  // aaa\3\3bbb
  // 如果是 compress_off关闭压缩, 就是切分结果返回三个部分
  // 如果是 compress_on打开压缩, 就是切分结果两个部分，把两个相邻的\3合并成了一个
  // vim中输入不可见字符【先按CTRL V，再按输入的数字（如3），然后按空格，最后删除空格【空格告诉我们输入完了】
  // 为什么此时把定义写到了头文件中，因为这样方便后续进行调用
  static void Split(const std::string& line,
      std::vector<std::string>* tokens,
      const std::string& split_char) {
    boost::split(*tokens, line,
        boost::is_any_of(split_char),
        boost::token_compress_off);
  }
};

class DictUtil {//作为一个处理字典的工具类，不仅可以加载暂停词表，还可以加载其他的词表
public:
  bool Load(const std::string& path) {//把暂停词表加载到内存之中（从文件中把数据加载到内存里面）
    std::ifstream file(path.c_str());//打开文件
    if (!file.is_open()) {//判断文件是否打开成功
      return false;
    }
    //主要完成按行读操作（因为暂停词是按行存储的，读取到一行就加载到一个哈希表）
    std::string line;
    while (std::getline(file, line)) {
      set_.insert(line);//读取到内容之后，把这一行插入到哈希表里面
    }
    file.close();//关闭
    return true;
  }

  bool Find(const std::string& key) const {//数据加载到内存之后就可以随时进行查找
    return set_.find(key) != set_.end();
    //查看输入参数是否在哈希表内部unordered_set的find函数返回：返回一个迭代器，
    //如果找到了，返回的迭代器一定不等于end；如果没找到就等于end
  }

private:
  std::unordered_set<std::string> set_;//只需要知道哪些词是暂停词，不需要value，所以使用unordered_set存储暂停词
};

class FileUtil {
public:
  static bool Read(const std::string& input_path,
      std::string* content) {
    std::ifstream file(input_path.c_str());
    if (!file.is_open()) {
      return false;
    }
    // 如何获取到文件的长度?//C++17之前，不能直接获得文件的长度
    file.seekg(0, file.end);//seekg把文件的光标移动到文件的末尾。
    //现在去读必须知道即将要读或者写的是第几个字节，可以通过随机值的方式随机进行修改
    ////先通过随机读的方式移动到文件的末尾   
    // [注意!!] 使用此方式来读文件, 文件最大不能超过2G:因为此时int是有符号的
    // tellg返回的是int型的
    int length = file.tellg();//获取文件的指针末尾，返回的结果是当前文件指针相对于文件开始位置的偏移量
    file.seekg(0, file.beg);//在让文件指针回到开头位置
    content->resize(length);//不用加1，\0是c风格字符串的编码习惯，之后的大数语言都不用考虑\0
    file.read(const_cast<char*>(content->data()), length);//第一个参数为读到的缓冲区，第二个参数为读到的长度
    file.close();
    return true;
  }

  static bool Write(const std::string& output_path,
      const std::string& content) {
    std::ofstream file(output_path.c_str());
    if (!file.is_open()) {//打开失败
      return false;
    }
    file.write(content.data(), content.size());//写入
    file.close();//关闭文件
    return true;
  }
};

class TimeUtil {
public:
  // 获取到秒级时间戳
  static int64_t TimeStamp() {
    struct ::timeval tv;
    ::gettimeofday(&tv, NULL);
    return tv.tv_sec;
  }

  // 获取到毫秒级时间戳
  static int64_t TimeStampMS() {
    struct ::timeval tv;
    ::gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
  }

  // 获取到微秒级时间戳
  static int64_t TimeStampUS() {
    struct ::timeval tv;
    ::gettimeofday(&tv, NULL);
    return tv.tv_sec * 1e6 + tv.tv_usec;
  }
};
}  // end common
